package com.leyou.gateway.filter;

import com.leyou.common.auth.domain.Payload;
import com.leyou.common.auth.domain.UserInfo;
import com.leyou.common.auth.utils.JwtUtils;
import com.leyou.common.constant.LyConstants;
import com.leyou.common.utils.CookieUtils;
import com.leyou.gateway.config.FilterProperties;
import com.leyou.gateway.config.JwtProperties;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.FORM_BODY_WRAPPER_FILTER_ORDER;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;

/**
 * 网关过滤器，凡是经过网关的请求，都会经过过滤器
 */
@Slf4j
@Component
@EnableConfigurationProperties({JwtProperties.class, FilterProperties.class})
public class AuthFilter extends ZuulFilter {

    @Autowired
    private JwtProperties jwtProp;

    @Autowired
    private FilterProperties filterProp;

    /**
     * 过滤器的类型，执行的时机
     * FilterConstants.PRE_TYPE 表示在处理器访问之前执行当前过滤操作
     * @return
     */
    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    /**
     * 过滤器执行的顺序，数字越小，越靠前
     * @return
     */
    @Override
    public int filterOrder() {
        return FORM_BODY_WRAPPER_FILTER_ORDER+1;
    }

    /**
     * 当前过滤器的开关，true表示当前过滤器可用，false表示不可用
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 过滤内容
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        log.info("【网关权限校验】开始！");
        //获取当前request请求
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        //获取当前请求的url路径
        String requestURI = request.getRequestURI();
        //判断是否是白名单
        if(isWhite(requestURI, filterProp.getAllowPaths())){
            log.info("【网关权限校验】结束！当前请求在白名单中，无需登录！");
            //表示结束当前过滤业务，但是会继续当前请求，访问到处理器。
            return null;
        }
        //如果不是白名单获取token
        String token = CookieUtils.getCookieValue(request, jwtProp.getUser().getCookieName());
        //判断是否为空
        if(StringUtils.isBlank(token)){
            //如果要终止当前请求继续执行，但是当前方法依然会继续执行，直到出现return null，要使用如下的方式：
            ctx.setSendZuulResponse(false);
            //如果要给请求设置一个异常状态码，要使用如下的方式：403和401都是权限不足
            ctx.setResponseStatusCode(403);
            log.error("【网关权限校验】权限不足，请登录！");
            return null;
        }
        //解析token，查看token是否合法
        try {
            Payload<UserInfo> payload = JwtUtils.getInfoFromToken(token, jwtProp.getPublicKey(), UserInfo.class);
            //获取用户信息
            UserInfo userInfo = payload.getUserInfo();
            //得到用户的id
            Long userId = userInfo.getId();
            //得到用户的角色
            String userRole = userInfo.getRole();
            //将用户id添加到请求头中继续向下访问
            ctx.addZuulRequestHeader(LyConstants.USER_ID_KEY, userId.toString());
            //下面这句是错误写法！
            //UserHolder.setId(userId);
            //将用户角色添加到请求头中继续向下访问
            ctx.addZuulRequestHeader(LyConstants.USER_ROLE_KEY, userRole);
        }catch (Exception e){
            //如果要终止当前请求继续执行，但是当前方法依然会继续执行，直到出现return null，要使用如下的方式：
            ctx.setSendZuulResponse(false);
            //如果要给请求设置一个异常状态码，要使用如下的方式：403和401都是权限不足
            ctx.setResponseStatusCode(403);
            log.error("【网关权限校验】权限不足，token不合法！");
            return null;
        }
        log.info("【网关权限校验】结束！");
        //表示结束当前过滤业务，但是会继续当前请求，访问到处理器。
        return null;
    }

    //判断当前请求是否在白名单
    private boolean isWhite(String requestURI, List<String> allowPaths) {
        for (String allowPath : allowPaths) {
            //判断当前请求地址是否以白名单中的某一个开头
            if(requestURI.startsWith(allowPath)){
                return true;
            }
        }
        return false;
    }

}
